package com.example.provider.aop;

import com.alibaba.fastjson.JSON;
import com.example.provider.annotation.MyRedisCache;
import io.netty.util.internal.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * {缓存切面+ redisson分布式锁}
 *
 * @author KamaSevia
 * @version {版本号} 2020-12-31-12:55
 */
@Component
@Aspect
public class RedisCacheAspect {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(com.example.provider.annotation.MyRedisCache)")
    public Object cacheAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable{
        //获取连接点名称
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取连接点的MyCache注解信息
        MyRedisCache annotation = methodSignature.getMethod().getAnnotation(MyRedisCache.class);
        // 获取缓存的前缀
        String prefix = annotation.prefix();
        // 获取分布式锁的缓存时间
        int lockTime = annotation.lockTime();
        // 组装成key
        String key = prefix + Arrays.asList(joinPoint.getArgs()).toString();
        // 1. 查询缓存
        Object result = this.cacheHit(methodSignature, key);
        if (result != null) {
            System.out.println("走缓存,key" + key);
            return result;
        }
        // 初始化分布式锁
        RLock lock = this.redissonClient.getLock("myCacheKey");
        try {
            // 防止缓存击穿 加锁
            lock.lock(lockTime, TimeUnit.SECONDS);
            // 再次检查内存是否有，因为高并发下，可能在加锁这段时间内，已有其他线程放入缓存
            result = this.cacheHit(methodSignature, key);
        } finally {
            if (result != null) {
                lock.unlock();
                return result;
            }
        }

        try {
            // 并把结果放入缓存
            // 2. 执行查询的业务逻辑从数据库查询
            result = joinPoint.proceed(joinPoint.getArgs());
            this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result));
            // 释放锁
        } finally {
            lock.unlock();
        }
        return result;
    }

    private Object cacheHit(MethodSignature methodSignature, String cacheKey) {
        //查询缓存
        String cacheData = this.redisTemplate.opsForValue().get(cacheKey);
        if (!StringUtil.isNullOrEmpty(cacheData)) {
            // 缓存不是空，则反序列化，直接返回
            // 获取方法返回类型
            Class returnType = methodSignature.getReturnType();
            // 不能使用parseArray<cache, T>，因为不知道List<T>中的泛型
            return JSON.parseObject(cacheData, returnType);
        }
        return null;
    }
}
